﻿using System;
using System.Linq;
using System.Reflection;

namespace Affinity.Core.Utility
{
    public static class Delegates
    {
        public static Delegate CreateTypedDelegateForGeneralHandler(Type delegateType, Action<object[]> handler)
        {
            //return Delegate.CreateDelegate(delegateType, new MethodShim(handler), "InvokeMethod");
            var methodShim = new MethodShim(handler);
            return Delegate.CreateDelegate(delegateType, methodShim, methodShim.GetInvokeMethod(delegateType.GetMethod("Invoke").GetParameters().Select(parameter => parameter.ParameterType).ToArray()));
        }

        private class MethodShim
        {
            public MethodShim(Action<object[]> generalHandler)
            {
                _GeneralHandler = generalHandler;
            }

            public MethodInfo GetInvokeMethod(Type[] parameterTypes)
            {
                MethodInfo foundMethod = GetType().GetMethods().Where(method => method.Name == "InvokeMethod" && method.GetParameters().Length == parameterTypes.Length).Single();
                return parameterTypes.Length == 0 ? foundMethod : foundMethod.MakeGenericMethod(parameterTypes);
            }

            public void InvokeMethod()
            {
                _GeneralHandler(new object[] { });
            }

            public void InvokeMethod<TParam>(TParam param)
            {
                _GeneralHandler(new object[] { param });
            }

            public void InvokeMethod<TParam1, TParam2>(TParam1 param1, TParam2 param2)
            {
                _GeneralHandler(new object[] { param1, param2 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3>(TParam1 param1, TParam2 param2, TParam3 param3)
            {
                _GeneralHandler(new object[] { param1, param2, param3 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5, TParam6 param6)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5, param6 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5, TParam6 param6, TParam7 param7)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5, param6, param7 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5, TParam6 param6, TParam7 param7, TParam8 param8)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5, param6, param7, param8 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8, TParam9>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5, TParam6 param6, TParam7 param7, TParam8 param8, TParam9 param9)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5, param6, param7, param8, param9 });
            }

            public void InvokeMethod<TParam1, TParam2, TParam3, TParam4, TParam5, TParam6, TParam7, TParam8, TParam9, TParam10>(TParam1 param1, TParam2 param2, TParam3 param3, TParam4 param4, TParam5 param5, TParam6 param6, TParam7 param7, TParam8 param8, TParam9 param9, TParam10 param10)
            {
                _GeneralHandler(new object[] { param1, param2, param3, param4, param5, param6, param7, param8, param9, param10 });
            }

            private readonly Action<object[]> _GeneralHandler;
        }
    }
}
